{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Going beyond the limits of native integer types\n",
    "\n",
    "The methods and techniques used to provide privacy rely (in some cases) on working with very big integers. However, the platforms we are working with (PyTorch, TensorFlow, DL4J) all have limitations with the maximum size they can handle.\n",
    "\n",
    "To overcome this limitation, we are providing a new type, `LargePrecisionTensor` that uses an internal structure to represent these big numbers."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [],
   "source": [
    "import torch\n",
    "from syft.frameworks.torch.tensors.interpreters.large_precision import LargePrecisionTensor"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [],
   "source": [
    "import syft\n",
    "from syft import TorchHook\n",
    "hook = TorchHook(torch)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [],
   "source": [
    "tensor = torch.tensor([3.0])"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [],
   "source": [
    "x = LargePrecisionTensor(tensor)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "(Wrapper)>LargePrecisionTensor>tensor([3.])"
      ]
     },
     "execution_count": 5,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "x.on(tensor)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "We can fix the precision of this tensor with regular values. This in PyTorch can be done with precisions below 64 bits."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "(Wrapper)>FixedPrecisionTensor>tensor([3000])"
      ]
     },
     "execution_count": 6,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "tensor.fix_prec()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Let's make this tensor larger. `precision_fractional` is the desired precision. It'll be a very big number, so big that PyTorch cannot hold it in any of its types.\n",
    "\n",
    "We also tell the function `fix_prec()` which internal type we'll use to represent these large numbers from thoses available in the platform."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\n",
      "Adding number 30000000000000000903829797004216275086714596193240853864963239397098224001329386984633370688074372907047315290515183484539008024100591320340654985059867159958835993685232641400781191169686886680672647790808531134864424057208559359749288237392529192038432768 for item 3.0\n",
      "\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "(Wrapper)>LargePrecisionTensor>tensor([[ 15, 251, 230,  31, 150, 207, 109,   0,   0,   0,   0,   0,   0,   0,\n",
       "           0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,\n",
       "           0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,\n",
       "           0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,\n",
       "           0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,\n",
       "           0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,\n",
       "           0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,\n",
       "           0,   0,   0,   0,   0,   0,   0,   0,   0]], dtype=torch.int16)"
      ]
     },
     "execution_count": 7,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "large_tensor = tensor.fix_prec(internal_type=torch.int16, precision_fractional=256, verbose=True)\n",
    "large_tensor"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "And here we have our `tensor[3.]` represented as an integer number of 256 bits. Internally we have built a new Torch tensor that can restore the original data by using the precision and virtual precision.\n",
    "What is more, we can use these internal representations to make operations!"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Restoring the original value"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "We can restore the original number by applying `float_precision()` to our very large tensor"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "tensor([3.])"
      ]
     },
     "execution_count": 8,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "original = large_tensor.float_precision()\n",
    "original"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Operating with large precision tensors"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 20,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\n",
      "Adding number 999999999999999928773840520366757536876739320811576612231781480701470095354527494007746341441138276442474389769547563525432293116501122567178714359381222777104854460745804679379644497043208267383631647167377861948545889974808961869943571076775428108923489484800 for item 100000.0\n",
      "\n",
      "\n",
      "Adding number 200000000000000006025531980028108500578097307954939025766421595980654826675529246564222471253829152713648768603434556563593386827337275468937699900399114399725573291234884276005207941131245911204484318605390207565762827048057062398328588249283527946922885120 for item 20.0\n",
      "\n"
     ]
    }
   ],
   "source": [
    "precision = torch.int16\n",
    "virtual_prec = 256\n",
    "\n",
    "x1 = torch.tensor([100000.])\n",
    "x2 = torch.tensor([20.])\n",
    "lpt1 = x1.fix_prec(internal_type=precision, precision_fractional=virtual_prec, verbose=True)\n",
    "lpt2 = x2.fix_prec(internal_type=precision, precision_fractional=virtual_prec, verbose=True)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Let's add them. We should have a result tensor containing `100020.`"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 21,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "(Wrapper)>LargePrecisionTensor>tensor([[  8,  33, 169, 229, 249, 252, 173, 188,  44,   0,   0,   0,   0,   0,\n",
       "           0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,\n",
       "           0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,\n",
       "           0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,\n",
       "           0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,\n",
       "           0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,\n",
       "           0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,\n",
       "           0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0]],\n",
       "       dtype=torch.int16)"
      ]
     },
     "execution_count": 21,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "result_lpt = lpt1 + lpt2\n",
    "result_lpt"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "tensor([100020.])"
      ]
     },
     "execution_count": 11,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "result_lpt.float_precision()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "We have added numbers with a precision of 10^256!"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "What about multiplication?"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 18,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "(Wrapper)>LargePrecisionTensor>tensor([[  4,  68,  61,  23, 195, 236,   8, 192,   0,   0,   0,   0,   0,   0,\n",
       "           0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,\n",
       "           0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,\n",
       "           0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,\n",
       "           0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,\n",
       "           0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,\n",
       "           0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,\n",
       "           0,   0,   0,   0,   0,   0,   0,   0,   0,   0]], dtype=torch.int16)"
      ]
     },
     "execution_count": 18,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "precision = torch.int16\n",
    "virtual_prec = 256\n",
    "\n",
    "x1 = torch.tensor([10.])\n",
    "x2 = torch.tensor([20.5])\n",
    "lpt1 = x1.fix_prec(internal_type=precision, precision_fractional=virtual_prec)\n",
    "lpt2 = x2.fix_prec(internal_type=precision, precision_fractional=virtual_prec)\n",
    "\n",
    "result_lpt = lpt1 * lpt2\n",
    "result_lpt"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 19,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "tensor([205.])"
      ]
     },
     "execution_count": 19,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "result_lpt.float_precision()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "We have multiplied numbers represented as 10^256!"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Congratulations!!! - Time to Join the Community!\n",
    "\n",
    "Congratulations on completing this notebook tutorial! If you enjoyed this and would like to join the movement toward privacy preserving, decentralized ownership of AI and the AI supply chain (data), you can do so in the following ways!\n",
    "\n",
    "### Star PySyft on GitHub\n",
    "\n",
    "The easiest way to help our community is just by starring the Repos! This helps raise awareness of the cool tools we're building.\n",
    "\n",
    "- [Star PySyft](https://github.com/OpenMined/PySyft)\n",
    "\n",
    "### Join our Slack!\n",
    "\n",
    "The best way to keep up to date on the latest advancements is to join our community! You can do so by filling out the form at [http://slack.openmined.org](http://slack.openmined.org)\n",
    "\n",
    "### Join a Code Project!\n",
    "\n",
    "The best way to contribute to our community is to become a code contributor! At any time you can go to PySyft GitHub Issues page and filter for \"Projects\". This will show you all the top level Tickets giving an overview of what projects you can join! If you don't want to join a project, but you would like to do a bit of coding, you can also look for more \"one off\" mini-projects by searching for GitHub issues marked \"good first issue\".\n",
    "\n",
    "- [PySyft Projects](https://github.com/OpenMined/PySyft/issues?q=is%3Aopen+is%3Aissue+label%3AProject)\n",
    "- [Good First Issue Tickets](https://github.com/OpenMined/PySyft/issues?q=is%3Aopen+is%3Aissue+label%3A%22good+first+issue%22)\n",
    "\n",
    "### Donate\n",
    "\n",
    "If you don't have time to contribute to our codebase, but would still like to lend support, you can also become a Backer on our Open Collective. All donations go toward our web hosting and other community expenses such as hackathons and meetups!\n",
    "\n",
    "[OpenMined's Open Collective Page](https://opencollective.com/openmined)"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python [conda env:openmined2]",
   "language": "python",
   "name": "conda-env-openmined2-py"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.7.2"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
